/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * TrainingBook
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

 
#include <lap-header.hpp>
#include <filesystem-utils.hpp>
#include <fstream>
#include <cassert>

///@brief Default constructor
LapHeader::LapHeader()
{
    fcMoy = 0;
    fcMax = 0;
    distance = 0.;
    calories = 0.;
    for(unsigned int i = 0 ; i < 7 ; i++)
        intensity[7] = 0.;
    cadence = 0;
    maxSpeed = 0.;
    power = 0.;
}

///@brief Copy constructor
///@param l LapHeader object to copy
LapHeader::LapHeader(const LapHeader& l)
{
    date = l.date;
    distance = l.distance;
    duration = l.duration;
    fcMoy = l.fcMoy;
    fcMax = l.fcMax;
    calories = l.calories;
    for(unsigned int i = 0 ; i < 7 ; i++)
        intensity[i] = l.intensity[i];
    maxSpeed = l.maxSpeed;
    cadence = l.cadence;
    power = l.power;
}

///@brief Destructor
LapHeader::~LapHeader()
{

}

///@brief Overload of = operator
///@param l LapHeader object to copy
LapHeader &LapHeader::operator=(const LapHeader& l)
{
    date = l.date;
    distance = l.distance;
    duration = l.duration;
    fcMoy = l.fcMoy;
    fcMax = l.fcMax;
    calories = l.calories;
    for(unsigned int i = 0 ; i < 7 ; i++)
        intensity[i] = l.intensity[i];
    maxSpeed = l.maxSpeed;
    cadence = l.cadence;
    power = l.power;
    return (*this);
}
        
///SETTERS
        
///@brief Set the distance value
///@param ddist Distance value to set
void LapHeader::SetDistance(double ddist)
{
    distance = ddist;
}

///@brief Set the duration value
///@param dduration Duration value to set
void LapHeader::SetDuration(Duration dduration)
{
    duration = dduration;
}

///@brief Set the date value
///@param ddate Date value to set
void LapHeader::SetDateTime(DateTime ddate)
{
    ddate = date;
}

///@brief Set FC Moy value
///@param dFCMoy FC moy value to set
void LapHeader::SetFCMoy(unsigned int dFCMoy)
{
    fcMoy = dFCMoy;
}

///@brief Set FC Max value
///@param dFCMax FC max value to set
void LapHeader::SetFCMax(unsigned int dFCMax)
{
    fcMax = dFCMax;
}

///@brief Set the consumed calories
///@param dcalories Calories value to set
void LapHeader::SetCalories(unsigned int dcalories)
{
    calories = dcalories;
}

///@brief Set the time spent in a given intensity zone
///@param zone Index of intensity zone
///@param time Time spend in intensity zone
void LapHeader::SetTimeInZone(unsigned int zone, double time)
{
    assert(zone >= 1);
    assert(zone <= 7);
    intensity[zone - 1] = time;
}
        
///@brief Set the maximum speed
///@param dspeed Maximum speed value to set
void LapHeader::SetMaximumSpeed(double dspeed)
{
    maxSpeed = dspeed;
}
        
///GETTERS
        
///@brief Return the distance
double LapHeader::GetDistance()
{
    return distance;
}
        
///@brief Return the duration
Duration LapHeader::GetDuration()
{
    return duration;
}

///@brief Return the date
DateTime LapHeader::GetDateTime() const
{
    return date;
}

///@brief Return FC moy
unsigned int LapHeader::GetFCMoy()
{
    return fcMoy;
}

///@brief Return FC max
unsigned int LapHeader::GetFCMax()
{
    return fcMax;
}

///@brief Return consumed calories
unsigned int LapHeader::GetCalories()
{
    return calories;
}

///@brief Return time spent in a given intensity zone
///@param zone Index of the zone of interest
double LapHeader::GetTimeInZone(unsigned int zone)
{
    assert(zone >= 1);
    assert(zone <= 7);
    return intensity[zone-1];
}

///@brief Return the maximum speed
double LapHeader::GetMaximumSpeed()
{
    return maxSpeed;
}

///@brief Return the power
double LapHeader::GetPower()
{
    return power;
}

///@brief return the cadence
unsigned int LapHeader::GetCadence()
{
    return cadence;
}
        
///IMPORT FROM DIFFERENT TYPES OF FILES
///@brief Read lap header from a XML node in a Tcx File
///@param lapNode XML node to parse
void LapHeader::ReadFromTcxFile(xmlpp::Node* lapNode)
{
    if(lapNode->get_name() == "Lap")
    {
        const xmlpp::Element* lapNodeElement = dynamic_cast<const xmlpp::Element*> (lapNode);
        const xmlpp::Element::AttributeList &lapNodeAttributes = lapNodeElement->get_attributes();
        for(xmlpp::Element::AttributeList::const_iterator iterLapNodeAttributes = lapNodeAttributes.begin();
            iterLapNodeAttributes != lapNodeAttributes.end();
            ++iterLapNodeAttributes)
        {
            const xmlpp::Attribute* lapAttribute = *iterLapNodeAttributes;
            if(lapAttribute->get_name() == "StartTime")
            {
                ///@warning Time is given in UTC zone. For France, we should add +1 hour in winter and +2 hours in summer
                date.SetWithFormat(lapAttribute->get_value(), "yyyy-mm-ddThh:nn:ss.000Z");
            }
        }
        // Parsing lap node children
        xmlpp::Node::NodeList lapNodeChildren = lapNode->get_children();
        for(xmlpp::Node::NodeList::iterator iterLapNodeChildren = lapNodeChildren.begin();
            iterLapNodeChildren != lapNodeChildren.end();
            ++iterLapNodeChildren)
        {
            xmlpp::Node* lapNodeChild = dynamic_cast<xmlpp::Node*>(*iterLapNodeChildren);
            if(lapNodeChild->get_name() == "TotalTimeSeconds")
            {
                std::string durationString = ((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content();
                std::istringstream stream(durationString);
                double durationSeconds;
                stream >> durationSeconds;
                if(stream.fail())
                    std::cerr << "Problem while reading TotalTimeSeconds value\n";
                Duration duration;
                duration.SetSecond ((unsigned int)durationSeconds);
                duration.Normalize();
                
                SetDuration(duration);
            }
            if(lapNodeChild->get_name() == "DistanceMeters")
            {
                std::string distanceString = ((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content();
                std::istringstream stream(distanceString);
                stream >> distance;
                if(stream.fail())
                    std::cerr << "Problem while reading DistanceMeters value\n";
                
                distance /= 1000.;
            }
            if(lapNodeChild->get_name() == "MaximumSpeed")
            {
                std::string maximumSpeedString = ((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content();
                std::istringstream stream(maximumSpeedString);
                stream >> maxSpeed;
                if(stream.fail())
                    std::cerr << "Problem while reading MaximumSpeed value\n";
                                
                maxSpeed *= 3.6;
            }
            if(lapNodeChild->get_name() == "Calories")
            {
                std::string caloriesString = ((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content();
                std::istringstream stream(caloriesString) ;
                stream >> calories;
                if(stream.fail())
                    std::cerr << "Problem while reading Calories value\n";
            }
            if(lapNodeChild->get_name() == "Cadence")
            {
                std::string cadenceString = ((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content();
                std::istringstream stream(cadenceString) ;
                stream >> cadence;
                if(stream.fail())
                    std::cerr << "Problem while reading Cadence value\n";
            }
            if((lapNodeChild->get_name() == "AverageHeartRateBpm")||(lapNodeChild->get_name() == "MaximumHeartRateBpm"))
            {
                // Parsing node children
                xmlpp::Node::NodeList bpmNodeChildren = lapNodeChild->get_children();
                for(xmlpp::Node::NodeList::iterator iterBpmNodeChildren = bpmNodeChildren.begin();
                    iterBpmNodeChildren != bpmNodeChildren.end();
                    ++iterBpmNodeChildren)
                {
                    xmlpp::Node* bpmNodeChild = dynamic_cast<xmlpp::Node*>(*iterBpmNodeChildren);
                    if(bpmNodeChild->get_name() == "Value")
                    {
                        std::string bpmString = ((xmlpp::TextNode*)(*((bpmNodeChild->get_children()).begin())))->get_content();
                        std::istringstream stream(bpmString);
                        if(lapNodeChild->get_name() == "AverageHeartRateBpm")
                            stream >> fcMoy;
                        else if (lapNodeChild->get_name() == "MaximumHeartRateBpm")
                            stream >> fcMax;
                        if(stream.fail())
                            std::cerr << "Problem while reading AverageHeartRateBpm or MaximumHeartRateBpm value\n";
                    }
                }
                
            }
            
        }
    }
}


///@brief Read activity header form a trainingbook XML file
///@param lapNode Lap header node to read
void LapHeader::ReadXMLNode(xmlpp::Node* lapNode)
{
    if(lapNode->get_name() == "Lap")
    {
        xmlpp::Node::NodeList lapNodeChildren = lapNode->get_children();
        for(xmlpp::Node::NodeList::iterator iterLapNodeChildren = lapNodeChildren.begin();
            iterLapNodeChildren != lapNodeChildren.end(); ++iterLapNodeChildren)
        {
            xmlpp::Node* lapNodeChild = dynamic_cast<xmlpp::Node*>(*iterLapNodeChildren);
            if(lapNodeChild->get_name() == "Date")
                date.SetWithFormat(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content() , "yyyy-mm-dd--hh:nn:ss");
            else if(lapNodeChild->get_name() == "Distance")
                distance = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "Duration")
                duration.SetWithFormat(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content() , "h:nn:ss");
            else if(lapNodeChild->get_name() == "FCMoy")
                fcMoy = StringToUInt(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "FCMax")
                fcMax = StringToUInt(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "Calories")
                calories = StringToUInt(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MinutesInI1")
                intensity[0] = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MinutesInI2")
                intensity[1] = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MinutesInI3")
                intensity[2] = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MinutesInI4")
                intensity[3] = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MinutesInI5")
                intensity[4] = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MinutesInI6")
                intensity[5] = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MinutesInI7")
                intensity[6] = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "MaxSpeed")
                maxSpeed = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "Cadence")
                cadence = StringToUInt(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
            else if(lapNodeChild->get_name() == "Power")
                power = StringToDouble(((xmlpp::TextNode*)(*((lapNodeChild->get_children()).begin())))->get_content());
        }
    }
}
        
///WRITE IN DIFFERENT TYPES OF FILE
///@brief Write to XML native file
///@param fout out stream to XML file
void LapHeader::WriteXML(std::ofstream &fout)
{
    fout << "        <Date>" << date << "</Date>\n";
    fout << "        <Distance>" << distance << "</Distance>\n";
    fout << "        <Duration>" << duration << "</Duration>\n";
    fout << "        <FCMoy>" << fcMoy << "</FCMoy>\n";
    fout << "        <FCMax>" << fcMax << "</FCMax>\n";	
    fout << "        <Calories>" << calories << "</Calories>\n";
    fout << "        <MinutesInI1>" << GetTimeInZone(1) << "</MinutesInI1>\n";
    fout << "        <MinutesInI2>" << GetTimeInZone(2) << "</MinutesInI2>\n";
    fout << "        <MinutesInI3>" << GetTimeInZone(3) << "</MinutesInI3>\n";
    fout << "        <MinutesInI4>" << GetTimeInZone(4) << "</MinutesInI4>\n";
    fout << "        <MinutesInI5>" << GetTimeInZone(5) << "</MinutesInI5>\n";
    fout << "        <MinutesInI6>" << GetTimeInZone(6) << "</MinutesInI6>\n";
    fout << "        <MinutesInI7>" << GetTimeInZone(7) << "</MinutesInI7>\n";
    fout << "        <MaxSpeed>" << maxSpeed << "</MaxSpeed>\n";
    fout << "        <Cadence>" << cadence << "</Cadence>\n";
    fout << "        <Power>" << power << "</Power>\n";
}
        
///@brief Overload of < operator to sort laps depending on their date
///@param l1 LapHeader left hand operand
///@param l2 LapHeader right hand operand
bool operator < (const LapHeader& l1, const LapHeader& l2)
{
    return (l1.GetDateTime() < l2.GetDateTime());
}